
import java.util.Random;

public class CannonDynamics {
	final int HEIGHT=300, DEPTH=75, BALL_SIZE = 14, CANNON_LENGTH = 80, FIELD_LENGTH = 500;
	final float MAX_AMMO = 100f;
	final float G = 198.1f, FRICTION=1, UNCERTAINTY=0.1f, IMPACT_FACTOR=0.02f;
	final float TIMESTEP=0.1f;

	Random rand = new Random();
	boolean chooseAmmoRandom;

	// state
	double angle=Math.PI/4, ammunition=250; // cannon
	int angleDegrees;
	double posX, posY, vX, vY; // ball
	float[] ground = new float[FIELD_LENGTH];
	int[] groundDraw = new int[FIELD_LENGTH+2];
	boolean fire=true;
	int ctr=0;

	public CannonDynamics(boolean chooseAmmoRandom){
		this.chooseAmmoRandom = chooseAmmoRandom;
		for(int i=0;i<FIELD_LENGTH;i++)
			groundDraw[i]=HEIGHT;
		groundDraw[FIELD_LENGTH] = HEIGHT + DEPTH;
		groundDraw[FIELD_LENGTH+1] = HEIGHT + DEPTH;
		newShot();
	}

	void nextState(){
		ctr++;
		if (ctr>3){
			if (ctr>5)
				fire=false;
			double friction = FRICTION * vY * vY;
			vY += - TIMESTEP * G;// + (vY < 0 ? friction : -friction) + (rand.nextDouble() - 0.5) * UNCERTAINTY;
			friction = FRICTION * vX * vX;
	//		vX += (vX < 0 ? friction : -friction) + (rand.nextDouble() - 0.5) * UNCERTAINTY;

			// update position


			if (checkHeight()){
				// shot with angleDegrees and ammunition arrived at posX, posY
				newShot();
			}
		}

	}
	void newShot(){
		angleDegrees = 15 + rand.nextInt(70);
		angle = ((double)angleDegrees) /180. * Math.PI;
		if (chooseAmmoRandom)
			ammunition = 30+rand.nextInt(220);
		posX = CANNON_LENGTH * Math.cos(angle);
		posY = CANNON_LENGTH * Math.sin(angle);
		vX = ammunition * Math.cos(angle);
		vY = ammunition * Math.sin(angle);
		ctr=0;
		fire=true;
	}
	boolean checkHeight(){
		float depth = - (float)(IMPACT_FACTOR * vY);
		if (posY < 0){
			if ((int)posX >= FIELD_LENGTH)
				return true;
			if (posY - BALL_SIZE < ground[(int)posX] - depth){
				float ballDepth = ground[(int)posX] - depth + BALL_SIZE;
				// impact !!
				int impact_size = BALL_SIZE + 2;
				for (int i=-impact_size; i <= impact_size; i++){
					double angle = Math.acos((double) Math.abs(i)/(double)impact_size);
					depth = (float) (ballDepth - impact_size * Math.sin(angle));
					if (((int)posX+i) < FIELD_LENGTH && ((int)posX+i) >= 0 && ground[(int)posX+i] > depth){
						ground[(int)posX+i]=depth;
						groundDraw[(int)posX+i]=HEIGHT-(int)depth;
					}
				}
				return true;
			}
		}
		return false;
	}
	public static void main(String[] args) {
		CannonDynamics dyn = new CannonDynamics(true);
		System.out.println("Ammo="+(int)dyn.ammunition+" angle="+dyn.angleDegrees);
		int i=0;
		double x=-1;
		do{
			x=dyn.posX;
			System.out.println("T"+i+"  ("+(int)dyn.posX+", "+(int)dyn.posY+")  v("+(int)dyn.vX+", "+(int)dyn.vY+")");
			dyn.nextState();
			i++;

		}while(x<=dyn.posX);
		System.out.println("=> Time="+i+" distance of cannon ball ="+(int)x);
	}
}
